from dateutil import parser
from datetime import datetime
from openpyxl.styles import Alignment
from pywinauto import Application
from config import *
from sql import MySQLDB
import hashlib,socket,psutil,base64
import re,time,json,string,random
import requests,openpyxl,pymysql
import os,pyautogui,asyncio



def getTimeStr():

    time_local = time.localtime(time.time())
    dt = time.strftime("%Y-%m-%d_%H-%M-%S", time_local)
    return dt

def getTimeStr2(ct):

    time_local = time.localtime(ct)
    dt = time.strftime("%Y-%m-%d %H:%M:%S", time_local)
    return dt

def getTimeTit():

    time_local = time.localtime(time.time())
    dt = time.strftime("%Y-%m-%d_%H-%M-%S", time_local)
    return dt

def convert_to_timestamp(time_str):
    # 使用 dateutil.parser 解析时间字符串
    try:
        dt = parser.parse(time_str)
        
        # 转换为时间戳
        timestamp = time.mktime(dt.timetuple())

        return int(timestamp)
    except Exception as e:
        return -1

def isContain(kw,tit):

    glwords=[re.escape(item) for item in kw.split()]
    flag0=True
    if tit and isinstance(tit,str):
        for gkw in glwords:
            if not re.search(r'\b{}\b'.format(gkw),tit,re.I):
                flag0=False
                break
        return flag0
    
    else:
        return False
    

def get_config(key):

    with open('config.json','r',encoding='utf-8') as f:
        jsdata=json.load(f)
    
    return jsdata.get(key)

def get_allconfig():
    
    with open('config.json','r',encoding='utf-8') as f:
        jsdata=json.load(f)
    
    return jsdata

def update_config(updata:dict):

    with open('config.json','r',encoding='utf-8') as f:
        jsdata=json.load(f)
    
    for k,v in updata.items():

        jsdata[k]=v

    with open('config.json','w',encoding='utf-8') as f:
        jsdata=json.dump(jsdata,f,ensure_ascii=False,indent=4)

def get_random_code(length=8):
    characters = string.ascii_letters + string.digits
    random_code = ''.join(random.choice(characters) for _ in range(length))
    return random_code


def list_databases(host='localhost', user='root', password='rootroot', port=3306):
    """
    连接 MySQL 并列出所有数据库。

    Args:
        host (str): 数据库主机地址，默认为 'localhost'。
        user (str): 数据库用户名，默认为 'root'。
        password (str): 数据库密码。
        port (int): 数据库端口，默认为 3306。

    Returns:
        list: 数据库名称的列表。如果连接失败或无数据库，则返回空列表。
    """

    system_databases = {
        'information_schema',
        'mysql',
        'performance_schema',
        'sys'
    }
    try:
        # 连接到 MySQL
        conn = pymysql.connect(
            host=host,
            user=user,
            password=password,
            port=port,
            charset='utf8mb4'
        )
        cursor = conn.cursor()

        # 执行命令以获取所有数据库
        cursor.execute("SHOW DATABASES")
        databases = [db[0] for db in cursor.fetchall() ]  # 提取数据库名称
        user_databases = [db for db in databases if db not in system_databases]

        return user_databases
    except pymysql.MySQLError as e:
        print(f"Error: {e}")
        return None
    finally:
        # 确保游标和连接关闭
        if 'cursor' in locals():
            cursor.close()
        if 'conn' in locals():
            conn.close()

def create_database(host, user, password, database_name, charset="utf8mb4", collation="utf8mb4_general_ci"):
    """
    使用 pymysql 创建 MySQL 数据库。
    
    参数:
        host (str): MySQL 服务器地址（例如 'localhost'）。
        user (str): MySQL 用户名。
        password (str): MySQL 密码。
        database_name (str): 要创建的数据库名称。
        charset (str): 数据库字符集（默认 'utf8mb4'）。
        collation (str): 数据库排序规则（默认 'utf8mb4_general_ci'）。
        
    返回:
        bool: 创建成功返回 True，失败返回 False。
    """
    try:
        # 连接到 MySQL 服务器
        connection = pymysql.connect(host=host, user=user, password=password)
        with connection.cursor() as cursor:
            # 创建数据库 SQL 语句
            create_db_query = (
                f"CREATE DATABASE IF NOT EXISTS {database_name} "
                f"CHARACTER SET {charset} COLLATE {collation};"
            )
            cursor.execute(create_db_query)
            print(f"数据库 '{database_name}' 创建成功！")
        return database_name
    except Exception as e:
        print(f"创建数据库时发生错误: {e}")
        return False
    finally:
        if 'connection' in locals() and connection.open:
            connection.close()

def get_days_diff_from_timestamps(timestamp1, timestamp2):  
    """  
    使用datetime模块计算两个时间戳之间的天数差  
  
    参数:  
    timestamp1 (int): 第一个时间戳（秒为单位）  
    timestamp2 (int): 第二个时间戳（秒为单位）  
  
    返回:  
    int: 两个时间戳之间的天数差  
    """  
    # 将时间戳转换为datetime对象  
    dt1 = datetime.fromtimestamp(timestamp1)  
    dt2 = datetime.fromtimestamp(timestamp2)  
  
    # 计算时间差  
    delta = dt2 - dt1  
  
    # 提取天数差  
    days_diff = delta.days  
  
    return days_diff

def replace_first_occurrence(lst, old_value, new_value):  
    try:  
        index = lst.index(old_value)  
        lst[index] = new_value  
    except ValueError:  
        print(f"Value '{old_value}' not found in the list.")  
    return lst

def md5_encrypt(text):
    # 创建一个 md5 对象
    md5 = hashlib.md5()
    
    # 更新 md5 对象，注意需要编码成 bytes
    md5.update(text.encode('utf-8'))
    
    # 获取加密后的十六进制表示的字符串
    return md5.hexdigest()

def check_port_in_use(port, host='127.0.0.1'):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        try:
            s.bind((host, port))
            return False  # 端口未被占用
        except socket.error:
            return True  # 端口已被占用

def is_process_running(pid=None, process_name=None):
    """
    检查指定的进程 ID 或进程名称是否正在运行。

    :param pid: 进程 ID（如果提供）
    :param process_name: 进程名称（如果提供）
    :return: 如果进程正在运行，则返回 True；否则返回 False
    """
    if pid is not None:
        # 检查进程 ID 是否有效
        return psutil.pid_exists(pid)
    
    if process_name is not None:
        # 遍历所有进程，检查是否存在指定名称的进程
        for proc in psutil.process_iter(['name']):
            if proc.info['name'].lower() == process_name.lower():
                return True
                
    return False

def get_yzm(cot_img):
    while True:
        if check_port_in_use(1688):
            break
        else:
            print('验证码监听未启动,等待启动')
            start_yzm()
            time.sleep(0.2)

    if isinstance(cot_img,str) and cot_img:
        cur_img=None
        headers={
            'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36'
        }
        for _ in range(3):
            try:
                res_img=requests.get(cot_img,headers=headers)
                if res_img.status_code==200:
                    cur_img=res_img.content
                    break
            except Exception as e:
                print(e)
        
        if not cur_img:
            return
        cot_img=cur_img

    lsf=base64.b64encode(cot_img)
    resyzm=requests.request('post','http://127.0.0.1:1688/VerificationCode.php',data=lsf,timeout=30)
    captchacode=(resyzm.text).strip()
    return captchacode

def sqlToExcel(fnameqz,sqlstr,params=(),dbpmap=None):
    db = MySQLDB(DB_PATH)
    rs=db.query(sqlstr,params,return_as_dict=True)
    
    wb=openpyxl.Workbook()
    ws=wb[wb.sheetnames[0]]

    ws.append(list(rs[0].keys()))
    if dbpmap:
        ws.append([dbpmap.get(k) if dbpmap.get(k) else k for k in rs[0].keys()])
    for r in rs:
        for k,v in r.items():
            if v and '_date' in k:
                dt = datetime.strptime(v, "%Y-%m-%dT%H:%M:%S.%fZ")
                date_str = dt.strftime("%Y-%m-%d")
                r[k]=date_str

        # 提取日期部分
        ws.append(list(r.values()))

    for row in ws.iter_rows(min_row=1, max_col=ws.max_column, max_row=ws.max_row):
        for cell in row:
            cell.alignment = Alignment(horizontal='left', vertical='center')

    current_time_str = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime(time.time()))
    fname=f'{fnameqz}_{current_time_str}.xlsx'
    wb.save(fname)
    return fname

def start_yzm():
    yzm_dir,exe_name= YZ_PATH
    exe_path = os.path.join(os.getcwd(), yzm_dir, exe_name)
    """杀掉所有 startYzm.exe 进程"""
    for proc in psutil.process_iter(['pid', 'name']):
        try:
            if proc.info['name'] and proc.info['name'].lower() == exe_name.lower():
                print(f"杀死进程: {proc.info['pid']} - {proc.info['name']}")
                proc.kill()
        except Exception:
            pass


    """启动 startYzm.exe 并自动点击弹窗"""
    if not os.path.exists(exe_path):
        print(f"找不到 {exe_path}")
        return

    # 2️⃣ 启动 exe
    print(f"启动 {exe_name} ...")
    os.startfile(exe_path)
    
    # 等待程序启动
    time.sleep(1)  # 根据程序启动速度调整

    # 3️⃣ 自动处理弹窗
    try:
        app = Application(backend='win32').connect(path=exe_path)
        
        # 弹窗1: 信息确认
        dlg = app.window(title_re="信息：")  # 修改为实际弹窗标题
        dlg.wait('visible', timeout=5)
        dlg['是'].click()  # 点击“是”按钮

        # 弹窗2: USEO验证码识别端
        dlg2 = app.window(title_re="USEO验证码识别端")  # 修改为实际弹窗标题
        dlg2.wait('visible', timeout=5)
        dlg2['启动'].click()  # 点击“启动”按钮
        time.sleep(3)
    except Exception as e:
        print("未找到弹窗，或自动点击失败:", e)
        # 备用方案: 按回车确认弹窗
        pyautogui.press('enter')
        time.sleep(0.2)
        pyautogui.press('enter')


async def yb_human_wait(min_seconds=3.0, max_seconds=5.0):
    """
    生成一个模拟人类操作的等待时间，返回一个 min~max 秒之间的浮点数。
    
    参数:
    - min_seconds: 最小等待时间（包含），默认为 1.0
    - max_seconds: 最大等待时间（包含），默认为 5.0
    - do_sleep: 是否实际调用 time.sleep()，默认 False
    
    返回:
    - 一个浮点数，表示等待秒数
    """
    wait_time = round(random.uniform(min_seconds, max_seconds), 2)

    # 模拟更自然的人类行为：加入一点波动（偏态分布）
    jitter = random.uniform(-0.2, 0.2)
    wait_time = max(min_seconds, min(max_seconds, round(wait_time + jitter, 2)))
    await asyncio.sleep(wait_time)
    return wait_time